package com.bstek.uflo.designer.security.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.bstek.uflo.designer.model.Mapping;
import com.bstek.uflo.designer.security.AuthorizeAttribute;
import com.bstek.uflo.designer.security.manager.SecurityManager;
import com.bstek.uflo.designer.security.model.NodeAttribute;
import com.bstek.uflo.designer.security.model.NodeEntry;
import com.bstek.uflo.designer.security.model.NodeSecurityAttribute;
import com.bstek.uflo.designer.security.model.ProcessAttribute;
import com.bstek.uflo.designer.security.model.ProcessEntry;
import com.bstek.uflo.designer.security.model.ProcessEntryAssignee;
import com.bstek.uflo.designer.security.service.SecurityService;
import com.bstek.uflo.process.assign.AssigneeProvider;
import com.bstek.uflo.utils.EnvironmentUtils;

/**
 * @author matt.yao@bstek.com
 * @since 1.0
 */
@Service(SecurityService.BEAN_ID)
public class SecurityServiceImpl implements SecurityService, ApplicationContextAware {

	@Autowired
	@Qualifier(SecurityManager.BEAN_ID)
	private SecurityManager securityManager;

	private ApplicationContext applicationContext;

	public ProcessEntry checkProcessSecurity(long processId, String username) throws Exception {
		Assert.notNull(processId);
		Assert.notNull(username);
		ProcessEntry processEntry = new ProcessEntry(processId, true, true);
		List<ProcessEntry> processEntryList = securityManager.findDbProcessEntry(processId);
		if (processEntryList != null) {
			for (ProcessEntry pe : processEntryList) {
				List<ProcessEntryAssignee> assigneeList = securityManager.findDbProcessEntryAssignee(pe.getId());
				Collection<String> users = this.getAssigneeUsers(assigneeList);
				boolean existUser = users.contains(username);
				if (existUser) {
					processEntry = pe;
					break;
				}
			}
		}
		if (StringUtils.hasText(processEntry.getId()) && processEntry.isModifiable()) {
			List<ProcessAttribute> processAttributeList = securityManager.findProcessAttribute(processEntry.getId());
			processEntry.setProcessAttributeList(processAttributeList);
			List<NodeEntry> nodeEntrylist = securityManager.findNodeEntryByProcessId(processEntry.getId(), processId);
			for (NodeEntry nodeEntry : nodeEntrylist) {
				List<NodeAttribute> nodeAttributeList = securityManager.findNodeAttributeByNodeType(nodeEntry.getId(), nodeEntry.getNodeType());
				nodeEntry.setNodeAttributeList(nodeAttributeList);
			}
			processEntry.setNodeEntrylist(nodeEntrylist);
		}
		processEntry.setNodeSecurityAttributeList(getNodeSecurityAttributes());
		return processEntry;
	}

	public List<NodeSecurityAttribute> getNodeSecurityAttributes() throws Exception {
		List<NodeSecurityAttribute> result = new ArrayList<NodeSecurityAttribute>();
		NodeSecurityAttribute nodeSecurityAttribute = null;
		for (AuthorizeAttribute authorizeAttribute : securityManager.getAuthorizeAttributes()) {
			String className = authorizeAttribute.getClass().getSimpleName();
			String nodeType = className.substring(0, className.indexOf(AuthorizeAttribute.class.getSimpleName())).toLowerCase();
			nodeSecurityAttribute = new NodeSecurityAttribute(nodeType);
			List<Mapping> mappingList = authorizeAttribute.getAttributes();
			for (Mapping mapping : mappingList) {
				nodeSecurityAttribute.getAttributes().add(mapping.getKey());
			}
			result.add(nodeSecurityAttribute);
		}
		return result;
	}

	public String getLoginUsername() throws Exception {
		return EnvironmentUtils.getEnvironment().getLoginUser();
	}

	private Collection<String> getAssigneeUsers(List<ProcessEntryAssignee> assigneeList) {
		Set<String> result = new HashSet<String>();
		for (ProcessEntryAssignee assignee : assigneeList) {
			AssigneeProvider assigneeProvider = (AssigneeProvider) applicationContext.getBean(assignee.getAssigneeProviderId());
			Collection<String> coll = assigneeProvider.getUsers(assignee.getAssigneeId(), null, null);
			if (coll != null && coll.size() > 0) {
				result.addAll(coll);
			}
		}
		return result;
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

}
